﻿using System;
using System.Collections.Concurrent;
using System.Net.Sockets;

namespace EasySocket
{
    class SocketEventArgsManager : IDisposable
    {
        private readonly ConcurrentStack<SocketAsyncEventArgs> _readArgsStack;
        private readonly ConcurrentStack<SocketAsyncEventArgs> _writeArgsStack;
        private readonly BufferManager _bufferManager;
        private readonly EventHandler<SocketAsyncEventArgs> _rwIOComplete;

        public SocketEventArgsManager(int maxClientCount, int readBufferSize, EventHandler<SocketAsyncEventArgs> ioCompleted)
        {
            _readArgsStack = new ConcurrentStack<SocketAsyncEventArgs>();
            _writeArgsStack = new ConcurrentStack<SocketAsyncEventArgs>();
            _bufferManager = new BufferManager(maxClientCount * readBufferSize, readBufferSize);
            _rwIOComplete = ioCompleted;
        }

        public SocketAsyncEventArgs Rent(SocketEventTypeEnum argsType)
        {
            SocketAsyncEventArgs result = null;
            switch (argsType)
            {
                case SocketEventTypeEnum.Read:
                    lock (_readArgsStack)
                    {
                        if (!_readArgsStack.TryPop(out result))
                        {
                            result = new SocketAsyncEventArgs();
                            result.Completed += _rwIOComplete;
                        }
                        _bufferManager.SetBuffer(result);
                    }
                    break;
                case SocketEventTypeEnum.Write:
                    lock (_writeArgsStack)
                    {
                        if (!_writeArgsStack.TryPop(out result))
                        {
                            result = new SocketAsyncEventArgs();
                            result.Completed += _rwIOComplete;
                        }
                    }
                    break;
            }

            return result;
        }

        public void Return(SocketAsyncEventArgs args, SocketEventTypeEnum argsType)
        {
            if (args != null)
            {
                switch (argsType)
                {
                    case SocketEventTypeEnum.Read:
                        lock(_readArgsStack)
                        {
                            _bufferManager.FreeBuffer(args);
                            _readArgsStack.Push(args);
                        }
                        break;
                    case SocketEventTypeEnum.Write:
                        lock (_writeArgsStack)
                        {
                            _writeArgsStack.Push(args);
                        }
                        break;
                }
                args.UserToken = null;
            }
        }


        public void Dispose()
        {
            _bufferManager.Dispose();
            foreach(var item in _writeArgsStack)
            {
                item.Completed -= _rwIOComplete;
                item?.Dispose();
            }

            foreach (var item in _readArgsStack)
            {
                item.Completed -= _rwIOComplete;
                item?.Dispose();
            }
            _writeArgsStack.Clear();
            _readArgsStack.Clear();
        }
    }
}
